import {
  ImapStore,
  getLogger,
  MailEngine,
  Properties,
  Pop3Store,
  showDebugLog,
  memBufferCreator,
  base64EncodeStream,
  base64DecodeStream,
  IMail,
  IBuffer,
  joinBuffers,
  decodeCharset,
  OAuth2Outlook,
  PromptForUserAuthorize
} from "@coremail/mail_base"
import { ca } from "./ca"
import { storage, configList } from "./data"
import {
  SmtpTransport
} from '@coremail/mail_base/src/main/ets/transport/transport';
import util from '@ohos.util';

export {
  HostConfig,
} from '@coremail/mail_base'

const logger = getLogger('app');

showDebugLog(true);

class App {
  protocol: string = 'IMAP'
  properties: Properties = {
    ...configList[0],
    userInfo: {
      username: configList[0].email,
      password: configList[0].pass,
    }
  }

  constructor() {
  }

  setAccount(email: string) {
    const accountConfig = configList.filter(c => c.email == email);
    if (accountConfig.length > 0) {
      const config = accountConfig[0];
      storage.set("currentAccount", config);
      this.properties = {
        ...config,
        userInfo: {
          username: config.email,
          password: config.pass
        },
        ca: ca
      }
    }
  }

  getProperties(): Properties {
    return this.properties;
  }

  getImapStore(): ImapStore {
    const store = new ImapStore();
    store.setProperties(this.getProperties());
    return store;
  }

  getPop3Store(): Pop3Store {
    const store = new Pop3Store();
    store.setProperties(this.getProperties());
    return store;
  }

  getTransport(): SmtpTransport {
    const transport = new SmtpTransport();
    transport.setProperties(this.getProperties());
    return transport;
  }

  async testConnect(): Promise<boolean> {
    try {
      const store = this.getImapStore();
      await store.check();
      const transport = this.getTransport();
      await transport.check();
      const pop3Store = this.getPop3Store();
      await pop3Store.check();
      return true;
    } catch (e: unknown) {
      return false;
    }
  }

  async testGetFolderList(): Promise<void> {
    const store = this.getImapStore();
    const folderList = await store.getAllFolderList();
    logger.info("data", folderList);
    const f = folderList.filter(f => f.fullName.toLowerCase() == 'inbox')[0]
    const fd = await store.getFolderInfo('inbox');
    logger.info("folder", fd);
  }

  async testGetMail2(): Promise<void> {
    const engine = new MailEngine();
    const properties = this.getProperties();
    // properties.imap = undefined;
    engine.setProperties(properties);
    const store = engine.getStore();
    const inbox = await engine.getInbox()
    inbox.open();
    // const mailId = '790'

    const count = await inbox.getMailCount();
    const mailIdList = await store.getFolderMailIdList(inbox.getFullName(), count - 25, 25, {by: 'date', desc: true});
    let i = 0;
    for (const mailId of mailIdList) {

      try {
        const mailBasic = await store.getMailBasic(inbox.getFullName(), mailId);
        logger.info("html--- ", mailBasic);
      }
      catch (e: unknown) {
        logger.error("html--- ", 'get mail basic failed')
      }
      const mail = await inbox.getMail(mailId);
      await this.printMail(mail, i);
      i += 1;
    }
  }

  async printMail(mail: IMail, i: number): Promise<void> {
    logger.log(`mail ${i}`, await mail.getFrom());
    const subject = await mail.getSubject();
    const html = await mail.getHtml();
    const plain = await mail.getPlain();
    const date = await mail.getDate();
    const attachments = await mail.getAttachmentInfoList();
    const inlines = await mail.getInlineImageInfoList();
    const as: IBuffer[] = [];
    for (let i = 0; i < attachments.length; i++) {
      const a = await mail.getAttachment(i);
      as.push(a);
    }
    for (let i = 0; i < inlines.length; i++) {
      const a = await mail.getInlineImage(i);
      as.push(a);
    }
    const d = [
      await mail.getTo(),
      await mail.getCc(),
      await mail.getBcc(),
      await mail.getPriority(),
      await mail.isSeen(),
      await mail.isFlagged(),
      await mail.isAnswered(),
    ]
    logger.log("html--- ", i, '--', mail.getId(), '--', subject, '---', html.substring(0, 100), date.toUTCString());
    logger.log("html--- ", d, as.length);
    logger.log("html--- ", plain);
  }

  async testGetMail(): Promise<void> {
    const engine = new MailEngine();
    const properties = this.getProperties();
    // properties.imap = undefined;
    engine.setProperties(properties);
    const inbox = await engine.getInbox()
    await inbox.open();
    let count = await inbox.getMailCount();
    //count = Math.min(count, 10);
    const mails = await inbox.getMailList(count);
    logger.info('mails', mails);
    let i = 0;
    for (const mail of mails) {
      i += 1;
      try {
        logger.error("html--- mail id", mail.getId());
        if (mail.getId() != '768') {
          continue;
        }
        await this.printMail(mail, i);
      } catch (e: unknown) {
        logger.error("html--- error", e);
      }
    }
    logger.info("end");
    engine.getStore().release();
  }

  async testGetImapMailHeader(): Promise<void> {
    const store = this.getImapStore();
    const ids = await store.getFolderMailIdList("INBOX", 1, 3, {by: "date", desc: true});
    for (const id of ids) {
      const header = await store.getMailHeader("INBOX", id)
      logger.log("header", header);
    }
  }

  async testGetPop3MailHeader(): Promise<void> {
    const store = this.getPop3Store();
    const ids = await store.getFolderMailIdList("INBOX", 1, 3, {by: "date", desc: true});
    for (const id of ids) {
      const header = await store.getMailHeader("INBOX", id)
      logger.log("header");
      for (const [k, v] of header.entries()) {
        logger.log(`${k}: ${v}`)
      }
    }
  }

  async testSearch(): Promise<void> {
    const engine = new MailEngine();
    const properties = this.getProperties();
    // properties.imap = undefined;
    engine.setProperties(properties);
    const keyword = 'aaa';
    const inbox = await engine.getInbox();
    const result = await inbox.searchMail({
      fields: {
        from: keyword,
        to: null,
        subject: null,
        attachment: null
      }
    });
    logger.info("result", result);

    const store = this.getImapStore();
    const res = await store.searchMail({
      folder: 'inbox',
      fields: {
        subject: '加密',
        to: null,
        // to: 'ddyceshi'
      }
    })
    logger.info("res", res);
  }

  async testGetMailPriority(): Promise<void> {
    const engine = new MailEngine();
    engine.setProperties(this.getProperties());
    const inbox = await engine.getInbox()
    await inbox.open();
    let count = await inbox.getMailCount();
    const mails = await inbox.getMailList(2);
    for (const mail of mails) {
      const priority = await mail.getPriority();
      const messageId = await mail.getHeader("message-ID");
      logger.info("priority", priority, messageId);
    }
  }

  async testOAuth2(prompt: PromptForUserAuthorize): Promise<void> {
    const oauth = new OAuth2Outlook('04986468-6a48-4e75-b572-91679fe6756d');
    const token = await oauth.requestAccessToken(prompt);
    this.properties.oauth2 = token
    const store = this.getImapStore();
    try {
      await store.check();
      logger.info("success")
    } catch(e: unknown) {
      logger.error("failed")
    }
  }

  async testSendMail(): Promise<void> {
    const transport = this.getTransport();
    transport.sendMail(
      { email: this.getProperties().userInfo.username, name: '' },
      [{ email: 'hmtest1028@163.com', name: '' }],
      [],
      [],
      '主题',
      'rich text',
      'plain text',
      [],
      [],
      new Map([
        ['x-priority', '1']
      ])
    )
  }

  dict2array(d: {[key: string]: number}): number[] {
    const r: number[] = [];
    for (let i = 0; i < Object.keys(d).length; i++) {
      r[i] = d[i];
    }
    return r;
  }

  async testBase64(): Promise<void> {
    const buff = new Uint8Array(['a'.charCodeAt(0)]);
    const base64 = new util.Base64Helper();
    const u8a = await base64.decode(buff, util.Type.BASIC);
    logger.info("base64", u8a);
  }
}

const app = new App();
export function getApp() {
  return app;
}
